﻿/*========================== 
 * @author 郑金泉
 * @desc MVC配置信息类
 * ========================= */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using Obsidian.Utils;
using Obsidian.Runtime;

namespace Obsidian.Config
{
    public class MvcConfig
    {

        public const string PN_ACTION = "act";
        public const string PN_REFRESH_CACHE = "_rc";
        public const string HTML_CACHE_KEY_PREFIX = "_mvc_html_cache_";
        private static DateTime modified = DateTime.MinValue;
        private static string _defaultAction = null;
        private static MvcCtrlInfo mvcCtrl;
        private static MvcViewInfo mvcView;
        private static bool _cacheable = true;
        private static Dictionary<string, MvcCacheInfo> dictCaches = new Dictionary<string, MvcCacheInfo>(StringComparer.OrdinalIgnoreCase);

        public static string DefaultAction
        {
            get
            {
                CheckForChanged();
                return _defaultAction;
            }
        }

        public static MvcCtrlInfo CtrlInfo
        {
            get
            {
                CheckForChanged();
                return mvcCtrl;
            }
        }

        public static MvcViewInfo ViewInfo
        {
            get
            {
                CheckForChanged();
                return mvcView;
            }
        }

        public static bool Cacheable
        {
            get
            {
                CheckForChanged();
                return _cacheable;
            }
        }

        public static MvcCacheInfo GetCacheSetting(string cacheKey)
        {
            CheckForChanged();
            MvcCacheInfo cache;
            if (dictCaches.TryGetValue(cacheKey, out cache))
                return cache;
            else
                return null;
        }

        private static void CheckForChanged()
        {
            if (AppConfig.CheckForChanged() || !AppConfig.Modified.Equals(modified))
            {
                GetInfos();
                modified = AppConfig.Modified;
            }
        }

        private static void GetInfos()
        {
            XmlDocument xmlDoc = AppConfig.GetXml();

            XmlNode node = xmlDoc.SelectSingleNode("appConfig/mvc");
            if (node == null)
                throw new Exception("未设置mvc配置");
            string val;
            if (XmlUtil.TryGetAttrValue(node, "defaultAction", out val))
                _defaultAction = val;
            else
                _defaultAction = null;

            node = xmlDoc.SelectSingleNode("appConfig/mvc/ctrls");
            if (node == null)
                throw new Exception("未设置ctrls配置");

            mvcCtrl = new MvcCtrlInfo();
            if (!XmlUtil.TryGetAttrValue(node, "namespace", out val) || String.IsNullOrEmpty(val))
                throw new Exception("namespace属性不能为空");
            mvcCtrl.SetNamespace(val);
            if (!XmlUtil.TryGetAttrValue(node, "typeSuffix", out val) || String.IsNullOrEmpty(val))
                throw new Exception("typeSuffix属性不能为空");
            mvcCtrl.SetTypeSuffix(val);

            node = xmlDoc.SelectSingleNode("appConfig/mvc/views");
            if (node == null)
                throw new Exception("未设置views配置");

            mvcView = new MvcViewInfo();
            if (!XmlUtil.TryGetAttrValue(node, "home", out val) || String.IsNullOrEmpty(val))
                throw new Exception("home属性不能为空");
            mvcView.SetHome(val);
            if (XmlUtil.TryGetAttrValue(node, "suffix", out val))
                mvcView.SetSuffix(val);

            node = xmlDoc.SelectSingleNode("appConfig/mvc/caches");
            if (node != null)
            {
                bool cacheable;
                if (XmlUtil.TryGetAttrValue(node, "enabled", out val) &&
                    bool.TryParse(val, out cacheable))
                {
                    _cacheable = cacheable;
                }
            }

            dictCaches.Clear();
            lock (dictCaches)
            {
                XmlNodeList nodeList = xmlDoc.SelectNodes("appConfig/mvc/caches/cache");
                if (nodeList != null && nodeList.Count > 0)
                {
                    lock (dictCaches)
                    {
                        string host = WebUtil.GetCurrentHost();
                        foreach (XmlNode xn in nodeList)
                        {
                            MvcCacheInfo c = new MvcCacheInfo();
                            if (!XmlUtil.TryGetAttrValue(xn, "url", out val))
                                throw new Exception("MVC Cache配置 url属性不能为空");
                            c.SetUrl(val);

                            if (!XmlUtil.TryGetAttrValue(xn, "seconds", out val))
                                throw new Exception("MVC Cache配置 seconds属性不能为空");
                            int seconds;
                            if (!int.TryParse(val, out seconds))
                                throw new Exception("MVC Cache配置 seconds属性格式不正确");
                            if (seconds <= 0)
                                throw new Exception("MVC Cache配置 seconds属性必须大于等于0");
                            c.SetSeconds(seconds);

                            bool isMvcPath = true;
                            if (XmlUtil.TryGetAttrValue(xn, "isMvcPath", out val))
                            {
                                if (!bool.TryParse(val, out isMvcPath))
                                    isMvcPath = true;
                            }
                            c.SetIsMvcPath(isMvcPath);

                            string key;
                            if (isMvcPath)
                                key = CreateMvcPageCacheKey(host, c.Url);
                            else
                                key = CreateUrlCacheKey(host, c.Url);

                            Logger.Info("配置CacheKey:" + key);

                            dictCaches[key] = c;
                        }
                    }
                }
            }

        }


        internal static string CreateUrlCacheKey(string host, string url)
        {
            int n = url.IndexOf("/");
            if (url.StartsWith("http"))
                url = url.Substring(n + 1);
            else if (n == 0)
                url = url.Substring(1);

            StringBuilder sb = new StringBuilder();
            sb.Append(HTML_CACHE_KEY_PREFIX);
            sb.Append(":");
            sb.Append(host);
            sb.Append(":");
            sb.Append(url);

            int m = url.IndexOf("?");
            if (m > 0)
            {
                string action;
                string sortedParams = SortParams(url, false, out action);
                sb.Append(">");
                sb.Append(sortedParams);
            }

            return sb.ToString().ToLower();

        }


        /// <summary>
        /// 创建MVC页面缓存Key
        /// </summary>
        /// <param name="url"></param>
        /// <param name="action"></param>
        /// <returns></returns>
        internal static string CreateMvcPageCacheKey(string host, string url)
        {
            return CreateMvcPageCacheKey(host, url, null);
        }


        /// <summary>
        /// 创建MVC页面缓存Key
        /// </summary>
        /// <param name="url"></param>
        /// <param name="action"></param>
        /// <returns></returns>
        internal static string CreateMvcPageCacheKey(string host, string url, string action)
        {
            if (String.IsNullOrEmpty(host))
                throw new Exception("host参数不能为空");
            if (String.IsNullOrEmpty(url))
                throw new Exception("url参数不能为空");

            string sortedParams = null;
            if (String.IsNullOrEmpty(action))
            {
                sortedParams = SortParams(url, true, out action);
            }
            else
            {
                string tmpAction;
                sortedParams = SortParams(url, true, out tmpAction);
            }


            int i = url.LastIndexOf("/");
            if (i < 0)
                return null;
            i++;
            int j = url.LastIndexOf(".");
            if (j > 0 && j < i)
                return null;

            if (String.IsNullOrEmpty(action))
            {
                if (j > 0)
                    action = url.Substring(i, j - i);
                else
                    action = url.Substring(i);
            }

            StringBuilder sb = new StringBuilder();
            sb.Append(HTML_CACHE_KEY_PREFIX);
            sb.Append(":");
            sb.Append(host);
            sb.Append(":");
            sb.Append(action);
            if (!String.IsNullOrEmpty(sortedParams))
            {
                sb.Append(">");
                sb.Append(sortedParams);
            }

            return sb.ToString().ToLower();
        }

        private static string SortParams(string url, bool findAction, out string action)
        {
            action = null;
            int n = url.IndexOf("?");
            List<string> list = new List<string>();
            if (n > 0)
            {
                string str = url.Substring(n + 1);
                string[] arrStr = str.Split('&');
                string actStart = PN_ACTION + "=";
                string rcStart = PN_REFRESH_CACHE + "=";
                foreach (string s in arrStr)
                {
                    if (s.StartsWith(rcStart))
                        continue;
                    if (findAction && s.StartsWith(actStart))
                    {
                        action = s.Substring(actStart.Length);
                        continue;
                    }
                    list.Add(s.ToLower());
                }
            }
            list.Sort();
            return ArrayUtil.Join<string>(list, "&");
        }

        public class MvcCtrlInfo
        {
            private string _namespace;
            private string _typeSuffix;

            public string Namespace
            {
                get { return this._namespace; }
            }

            internal void SetNamespace(string val)
            {
                this._namespace = val;
            }

            public string TypeSuffix
            {
                get { return this._typeSuffix; }
            }

            internal void SetTypeSuffix(string val)
            {
                this._typeSuffix = val;
            }
        }

        public class MvcCacheInfo
        {
            private string _url;
            private int _seconds;
            private bool _isMvcPath;

            public string Url
            {
                get { return _url; }
            }

            internal void SetUrl(string url)
            {
                this._url = url;
            }

            public int Seconds
            {
                get { return _seconds; }
            }

            internal void SetSeconds(int seconds)
            {
                this._seconds = seconds;
            }

            public bool isMvcPath
            {
                get { return this._isMvcPath; }
            }

            internal void SetIsMvcPath(bool isMvcPath)
            {
                this._isMvcPath = isMvcPath;
            }
        }

        public class MvcViewInfo
        {
            private string _home;
            private string _suffix;

            public string Home
            {
                get { return this._home; }
            }

            internal void SetHome(string val)
            {
                this._home = val;
            }

            public string Suffix
            {
                get { return this._suffix; }
            }

            internal void SetSuffix(string val)
            {
                this._suffix = val;
            }
        }

    }
}
